
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;

using Swaf;
using Swaf.BizObj;
using Swaf.Container;
using Swaf.CodeGen;
using Swaf.DataAccess;
using Swaf.Resource;

namespace Swaf.DataAccess.AdoNet
{
	public class AdoNetDataAccessFactory : INameAddressSupport, ICatalogItem
	{
		protected static bool s_includeDebug = false;
		protected static bool s_createDllsInDir = true;
		protected static bool s_checkedForIncludeDebug = false;
		protected Type m_dataAccessType = null;

		static AdoNetDataAccessFactory()
		{
			try
			{
				IApplication app = Application.currentApp;
				string createDllsInDir = app.resMgr.getMacro("GenerateDarDlls");
				string idstr = app.resMgr.getMacro("IncludeDebugInfoInDars");
				if (!Cmn.IsEmpty(idstr))
					s_includeDebug = bool.Parse(idstr);
				if (!Cmn.IsEmpty(createDllsInDir))
					s_createDllsInDir = bool.Parse(createDllsInDir);
			}
			catch (Exception e)
			{
				throw new DataAccessException("Invalid true/false value provided for GenerateDarDllsInDir or IncludeDebugInfoInDars macro definition", e);
			}
		}

		public AdoNetDataAccessFactory(XmlElement req, XmlCatalogContainerNode.NameInfo catalogName)
		{
			IApplication app = Application.currentApp;
			IResourceManager resMgr = Application.globalApp.resMgr;

			string reasonToCompile = "Config";
			if (s_createDllsInDir)
			{
				DirectoryInfo genbin = new DirectoryInfo(CodeClass.GenBinLocation);
				genbin.Create(); //Done to make sure directory exists.
				string dllName = string.Format("Swaf.DataAccess.{0}DataAccess.dll", catalogName.CatalogEntryName);
				FileInfo darDll = new FileInfo(Cmn.pathCombine(genbin.FullName, dllName));
				if (darDll.Exists)
				{
					bool useDarDll = false;
					if (catalogName.FileSystemFullFileName != null)
					{
						FileInfo darXml = new FileInfo(catalogName.FileSystemFullFileName);
						if (darXml.Exists)
						{
							DateTime darCreateTime = darXml.LastWriteTime;
							DateTime dllCreateTime = darDll.LastWriteTime;
							if (dllCreateTime > darCreateTime)
								useDarDll = true;
							else
								reasonToCompile = string.Format("dar xml {0} date {1} is newer than the dll {2}", darXml.FullName, darCreateTime, dllCreateTime);
						}
						else
							reasonToCompile = string.Format("dar xml {0} cannot be found", darXml.FullName);
					}
					else if (!Cmn.IsEmpty(app.resMgr.getMacro("RootRezNamespace")))
						useDarDll = true;

					if(useDarDll)
					{
						string compiledClassName = string.Format("Swaf.DataAccess.{0}DataAccess", catalogName.CatalogEntryName);
						System.Reflection.Assembly ass = System.Reflection.Assembly.LoadFrom(darDll.FullName);
						object darInstance = ass.CreateInstance(compiledClassName);
						m_dataAccessType = darInstance.GetType();
						app.Trace.write(Swaf.Logging.TraceLevels.Normal, "<AdoNetDataAccessReloadDll catalogName='{0}' />", catalogName.CatalogEntryName);
						return;
					}
				}
				else
					reasonToCompile = string.Format("dar dll {0} doesnt exist", darDll.FullName);
			}

			
			app.Trace.write(Swaf.Logging.TraceLevels.Normal, "<AdoNetDataAccessCreate catalogName='{0}' reason='{1}' />", catalogName.CatalogEntryName, reasonToCompile);

			//HACK: The follow is a hack to find the db type from the connection catalog.
			//It would be better to get that from the IConnectionItem interface, but that interface also 
			//creates an ado.net connection object and opens it when asked for.  For now this is 
			//a good enough way to get the db type.
			ICatalog dbCatalog = app.appData.get("DB") as ICatalog;
			string connectionId = resMgr.resolve(req.GetAttribute("connection"));
			XmlElement connectionInfo = dbCatalog.getCatalogContents(connectionId);
			string dbType = connectionInfo.GetAttribute("type");
			if(dbType == null || dbType == "")
				throw new DataAccessException(string.Format("No db type was specified in db connection catalog {0}.  This is needed for DataAccess catalog {1}.",
					connectionId, catalogName.CatalogEntryName));
			INameAddressSupport dbInfo = app.appData["AdoNetDbTypes." + dbType] as INameAddressSupport;
			if(dbInfo == null)
				throw new DataAccessException(string.Format("Invalid db type specified in db connection catalog {0}.  The type specified must be a know AdoNet provider and is needed for DataAccess catalog {1}.",
					connectionId, catalogName.CatalogEntryName));

			string dbElementType = dbInfo.get("EnumType") as string;
			string commandType = dbInfo.get("CommandType") as string;
			string transType = dbInfo.get("TransType") as string;
			string timeOut = req.GetAttribute("commandTimeout");
			if (timeOut == null || timeOut == "")
				timeOut = dbInfo.get("DefaultQueryTimeOut") as string;

            CodeClass cc = new CodeClass("Swaf.DataAccess.CodeTemplates.DataAccessTemplate", s_createDllsInDir, s_includeDebug); 
			cc.addReference("System.dll");
			cc.addReference("System.Data.dll");
			cc.addReference("System.Xml.dll");
			if (Cmn.IsEmpty(app.resMgr.getMacro("RootLoadAssembly")))
			{
				cc.addReference("$BaseBinLocation$SwafCore.dll");
			}
			else
			{
				cc.addReference("$BaseBinLocation$$RootLoadAssembly$.exe");
			}

			cc.CodeBlocks["$ClassName"].GeneratedContents = catalogName.CatalogEntryName + "DataAccess";
			cc.CodeBlocks["CreateParamsPre"].setFormatedContents(dbElementType);
			cc.CodeBlocks["DbCreate"].setFormatedContents("DB." + req.GetAttribute("connection"));
			cc.CodeBlocks["DbCommandType"].setFormatedContents(commandType, timeOut);
			cc.CodeBlocks["DbCommandType2"].setFormatedContents(commandType);
			cc.CodeBlocks["SqlTrans"].setFormatedContents(transType);
			cc.CodeBlocks["CreateSqlParam"].GeneratedContents = commandType;
			cc.CodeBlocks["AddParam"].setFormatedContents(commandType, dbElementType);
			XmlElement sqlInfo = getDbSqlElement(req, dbType, catalogName);
			if (string.Compare(sqlInfo.Name, "sql", true) == 0)
			{
				cc.CodeBlocks["CommandTypeStoredProc"].GeneratedContents = "";
			}
			else
			{
				//Nothing extra to do if it is a stored proc
			}

			StringBuilder buff = new StringBuilder();
			foreach (XmlNode n in sqlInfo.ChildNodes)
				if (n is XmlElement && string.Compare(n.Name, "code", true) == 0)
					buff.Append(n.InnerText);
				else
					buff.Append("sql.Append(@\"").Append(n.InnerText).Append("\");\n");
			cc.CodeBlocks["CreateSqlMainBody"].GeneratedContents = buff.ToString();

			XmlNodeList sqlParams = req.SelectNodes("Param");
			if (sqlParams.Count == 0)
				cc.CodeBlocks["CreateParam"].GeneratedContents = "";
			else for(int pos = 0; pos < sqlParams.Count; pos++)
			{
				XmlElement sqlParam = sqlParams[pos] as XmlElement;
				string dbName = sqlParam.GetAttribute("dbName");
				if (dbName == null || dbName == "")
					dbName = sqlParam.GetAttribute("name");
				NamedCodeBlock b = (pos == 0)?
					cc.CodeBlocks["CreateParam"].setFormatedContents(dbElementType, sqlParam.GetAttribute("type"), dbName, "0"):
					cc.CodeBlocks["CreateParam"].appendFormatedContents(dbElementType, sqlParam.GetAttribute("type"), dbName, "0");
				b.CodeBlocks["SetVal"].setFormatedContents(sqlParam.GetAttribute("name"));
				string dir = sqlParam.GetAttribute("direction");
				if (dir != null)
					b.CodeBlocks["Dir"].GeneratedContents = dir;
			}

			string fInfo = app.resMgr.resolve(req.GetAttribute("formatter"));
			if (fInfo == null || fInfo == "")
			{
				cc.CodeBlocks["GetResults"].GeneratedContents = "";
				cc.CodeBlocks["Formatter"].GeneratedContents = "";
			}
			else
			{
				cc.CodeBlocks["PutNoResults"].GeneratedContents = "";
				IClassFactory formatterFactory = app.factories["NewAdoNetFormatters"];
				INewDataFormatter formatter = formatterFactory.createFromPattern(fInfo, req, catalogName, this) as INewDataFormatter;

				if (formatter != null)
				{
					cc.CodeBlocks["Formatter"].setFormatedContents(formatter.FormatterClassName);
					cc.CodeBlocks["FormatterClass"].GeneratedContents = formatter.FormatterClassCode;
				}
				else
					throw new DataAccessException(string.Format("Invalid formatter '{0}' specified for DataAccess call {1}", fInfo, catalogName.CatalogEntryName));
			}

			string dataAccessClassName = cc.CodeBlocks["Namespace"].GeneratedContents + "." + cc.CodeBlocks["$ClassName"].GeneratedContents;
			try
			{
				object darInstance = cc.createInstance(dataAccessClassName);
				m_dataAccessType = darInstance.GetType();
			}
			catch (Exception e)
			{
				throw new DataAccessException(string.Format("Error while compiling generated DataAccess class '{0}'", dataAccessClassName), e);
			}
		}

		protected XmlElement getDbSqlElement(XmlElement root, string dbType, XmlCatalogContainerNode.NameInfo nameInfo)
		{
			XmlElement el = null;
			XmlNode n = root.SelectSingleNode(string.Format("*[@for='{0}']", dbType));
			if (n == null)
				n = root.SelectSingleNode("Sql");
			if (n == null)
				n = root.SelectSingleNode("StoredProc");
			if (n != null && n is XmlElement && (string.Compare(n.Name, "sql", true) == 0 || string.Compare(n.Name, "storedproc", true) == 0))
				el = n as XmlElement;
			if(el == null)
				throw new DataAccessException(string.Format("The data access catalog {0} does not contain a Sql or StoredProc element that is needed to do an AdoNet execute", nameInfo.CatalogEntryName));
			return el;
		}

		#region INameAddressSupport Members

		public object get(string name)
		{
            if (name == null)
                return this;

			return get(name, null);
		}

		public object get(string name, object oneArg)
		{
			if (oneArg == null) return this;

			if (m_dataAccessType != null)
			{
				string sql;
				IDbCommand cmd;
				DateTime st = DateTime.Now;
				object rc = ((AdoNetDataAccess)Cmn.createType(m_dataAccessType, new Type[0], new object[0], true)).execute(oneArg as INameAddressSupport, out sql, out cmd);
				DateTime et = DateTime.Now;

				TimeSpan t=et-st;
				bool longRunning = t.TotalMilliseconds > 30;
				if(!longRunning)
					Application.globalApp.Trace.write(Swaf.Logging.TraceLevels.Normal, "<AdoNetDataAccessGet name='{0}' time='{1}' />", m_dataAccessType.Name, t);
				if (longRunning)
				{
					Application.globalApp.Trace.write(Swaf.Logging.TraceLevels.Normal, "<AdoNetDataAccessGet name='{0}' time='{1}'>", m_dataAccessType.Name, t);
					Application.globalApp.Trace.write(Swaf.Logging.TraceLevels.Normal, "<Sql>{0}</Sql>", sql);
					foreach(IDbDataParameter p in cmd.Parameters)
						Application.globalApp.Trace.write(Swaf.Logging.TraceLevels.Normal, "<Param name='{1}'>{0}</Param>", p.Value, p.ParameterName);
					Application.globalApp.Trace.write(Swaf.Logging.TraceLevels.Normal, "</AdoNetDataAccessGet>");
				}
				return rc;
			}
			//if (m_generatedDataAccess != null)
			//	return m_generatedDataAccess.execute(oneArg as INameAddressSupport);
			return null;
		}

		public object get(string name, params object[] p)
		{
			return get(name, p.Length > 0?p[0]:null);
		}

		public void put(string name, object newValue)
		{
			if (m_dataAccessType != null)
			{
				string sql;
				IDbCommand cmd;
				DateTime st = DateTime.Now;
				((AdoNetDataAccess)Cmn.createType(m_dataAccessType, new Type[0], new object[0], true)).execute(newValue as INameAddressSupport, out sql, out cmd);
				DateTime et = DateTime.Now;

				TimeSpan t = et - st;
				bool longRunning = t.TotalMilliseconds > 800;
				if (!longRunning)
					Application.globalApp.Trace.write(Swaf.Logging.TraceLevels.Normal, "<AdoNetDataAccessPut name='{0}' time='{1}' />", m_dataAccessType.Name, t);
				if (longRunning)
				{
					Application.globalApp.Trace.write(Swaf.Logging.TraceLevels.Normal, "<AdoNetDataAccessPut name='{0}' time='{1}'>", m_dataAccessType.Name, t);
					Application.globalApp.Trace.write(Swaf.Logging.TraceLevels.Normal, "<Sql>{0}</Sql>", sql);
					foreach (IDbDataParameter p in cmd.Parameters)
						Application.globalApp.Trace.write(Swaf.Logging.TraceLevels.Normal, "<Param name='{1}'>{0}</Param>", p.Value, p.ParameterName);
					Application.globalApp.Trace.write(Swaf.Logging.TraceLevels.Normal, "</AdoNetDataAccessPut>");
				}
			}
		}

		public void put(string nameAddress, object val, object extraInfo)
		{
			put(nameAddress, val);
		}

		public bool isGetOnly
		{
			get { return false; }
		}

		public XmlSerializationSupport SerializationSupport
		{
			get { return XmlSerializationSupport.None; }
		}

		public string Xml
		{
			get { return "<xml/>"; }
		}

		public string getXml(string options)
		{
			return Xml;
		}

		public bool containsField(string nameAddress)
		{
			return false;
		}

		#endregion
		public System.Collections.IEnumerator GetEnumerator() { return null; }


		#region ICatalogItem Members
		protected string m_expiration = null;
		protected string m_key = null;
		protected string m_searchPath = null;
		protected string m_catalogName;

		public string catalogName
		{
			get
			{
				return m_catalogName;
			}
			set
			{
				m_catalogName = value;
			}
		}

		public string expiration
		{
			get
			{
				return m_expiration;
			}
			set
			{
				m_expiration = value;
			}
		}

		public string key
		{
			get
			{
				return m_key;
			}
			set
			{
				m_key = value;
			}
		}

		public string searchPath
		{
			get
			{
				return m_searchPath;
			}
			set
			{
				m_searchPath = value;
			}
		}

		public bool isCacheable
		{
			get { return true; }
		}

		#endregion

	}

	public enum MergeOption { none, merge, matchMerge };

	public class NewAdoNetBizObjFormatter : INewDataFormatter
	{
		protected static string s_findCodeXPath = "FormatterInfo/code[@for='{0}' and @on='{1}']";
		protected static Regex s_bizObjSpecPattern = null;
		protected static Regex s_MethodNameMatch = new Regex(@"^(\w+)\d$");
		protected CodeClass m_formatter;

		public NewAdoNetBizObjFormatter(Match mr, object[] extraInfo)
		{
			if (extraInfo != null && extraInfo.Length == 3 && extraInfo[0] is XmlElement && extraInfo[1] is XmlCatalogContainerNode.NameInfo && extraInfo[2] is AdoNetDataAccessFactory)
			{
				IApplication app = Application.currentApp;
				XmlElement req = extraInfo[0] as XmlElement;
				XmlCatalogContainerNode.NameInfo catalogName = extraInfo[1] as XmlCatalogContainerNode.NameInfo;
				AdoNetDataAccessFactory daFactory = extraInfo[2] as AdoNetDataAccessFactory;

				string returnType = mr.Groups[1].Value;
				string bizObjSpec = mr.Groups[2].Value;
				MergeOption merge = MergeOption.none;
				string mergeField = null;
				if (mr.Groups.Count >= 5)
				{
					merge = mr.Groups[3].Value.ToLower().StartsWith("match") ? MergeOption.matchMerge : MergeOption.merge;
					mergeField = mr.Groups[4].Value;
				}
				if(s_bizObjSpecPattern == null)
					lock(this.GetType())
						if(s_bizObjSpecPattern == null)
							s_bizObjSpecPattern = Application.currentApp.resMgr.knownGoodPattern(
								"{starts-with}{optional-ws}{optional ,}{optional-ws}({identifier}){optional-ws}{optional-balanced()}{optional-ws}");
				setupClass(returnType, merge, mergeField, bizObjSpec, null, null, req);
			}
		}

		protected NewAdoNetBizObjFormatter(string bizObjType, MergeOption merge, string mergeName, string bizObjSpec, IBizObjDefinition parent, string parentClassName, XmlElement req)
		{
			setupClass(bizObjType, merge, mergeName, bizObjSpec, parent, parentClassName, req);
		}

		protected void setupClass(string bizObjType, MergeOption merge, string mergeName, string bizObjSpec, IBizObjDefinition parent, string parentClassName, XmlElement req)
		{
			IApplication app = Application.currentApp;
			Match mr = s_bizObjSpecPattern.Match(bizObjSpec);
			if (mr.Success)
			{
				string name = mr.Groups[1].Value;
				string children = mr.Groups[2].Value;

				bool isList = string.Compare(bizObjType, "bizobjlist", true) == 0;
				IBizObjDefinition info = null;
				if (parent != null)
				{
					IBizObjField finfo = parent.getField(name, true);
					if (finfo != null)
					{
						if (finfo.BizType == BizFieldType.BizObjList)
						{
							info = ((IBizObjList)finfo.SampleValue).objectType;
							isList = true;
						}
						else if (finfo.BizType == BizFieldType.BizObj)
						{
							info = ((IBizObj)finfo.SampleValue).type;
							isList = false;
						}
						else if (finfo.BizType == BizFieldType.Method)
						{
							mr = s_MethodNameMatch.Match(name);
							if (mr.Success)
							{
								finfo = parent.getField(mr.Groups[1].Value, true);
								if (finfo != null)
								{
									if (finfo.BizType == BizFieldType.BizObjList)
									{
										info = ((IBizObjList)finfo.SampleValue).objectType;
										isList = true;
									}
									else if (finfo.BizType == BizFieldType.BizObj)
									{
										info = ((IBizObj)finfo.SampleValue).type;
										isList = false;
									}
								}
							}
						}
					}
				}
				else
					info = app.bizObjMgr.getDefinition(name);

				if (info == null)
					throw new BadBizObjTypeException(name);

				m_formatter = new CodeClass("Swaf.DataAccess.CodeTemplates.BizObjFormatterTemplate");
				m_formatter.CodeBlocks["Using"].GeneratedContents = "";
				m_formatter.CodeBlocks["$ClassName"].GeneratedContents = name + "Formatter";
				m_formatter.CodeBlocks["UpperName"].setFormatedContents(name.ToUpper());
				m_formatter.CodeBlocks["BizObjTypeName"].setFormatedContents(info.Name);
				m_formatter.CodeBlocks["ReaderColNames"].GeneratedContents = "";
				m_formatter.CodeBlocks["RequiresParentName"].GeneratedContents = (parent != null).ToString().ToLower();
				XmlNode n = req.SelectSingleNode(string.Format(s_findCodeXPath, "vars", name));
				m_formatter.CodeBlocks["CustomDek"].GeneratedContents = n== null?"":n.InnerText;
				n = req.SelectSingleNode(string.Format(s_findCodeXPath, "postEach", name));
				if (n == null)
					m_formatter.CodeBlocks["CustomBizObjProcessing"].GeneratedContents = "";
				else
					m_formatter.CodeBlocks["CustomBizObjProcessing"].CodeBlocks["Code"].GeneratedContents = n.InnerText;

				if (!isList)
				{
					m_formatter.CodeBlocks["formatBizObjList"].GeneratedContents = "";
					m_formatter.CodeBlocks["formatBizObjListMerge"].GeneratedContents = "";
					if (merge == MergeOption.none)
						m_formatter.CodeBlocks["formatBizObjMerge"].GeneratedContents = "";
					else
					{
						m_formatter.CodeBlocks["formatBizObjMerge"].setFormatedContents(mergeName);
						m_formatter.CodeBlocks["formatBizObj"].GeneratedContents = "";
					}

					m_formatter.CodeBlocks["FormatReturnList"].GeneratedContents = "";
					m_formatter.CodeBlocks["getBizObjMerge"].GeneratedContents = "";
					m_formatter.CodeBlocks["getBizObjList"].GeneratedContents = "";
					m_formatter.CodeBlocks["getBizObjList2"].GeneratedContents = "";
					m_formatter.CodeBlocks["BizObjListDek"].GeneratedContents = "";
					m_formatter.CodeBlocks["MergeBizObjListDek"].GeneratedContents = "";
					if (parent == null)
						m_formatter.CodeBlocks["getBizObjObj"].GeneratedContents = "";
					else
						m_formatter.CodeBlocks["getBizObjObj"].CodeBlocks["HasParent"].setFormatedContents(name);
					m_formatter.CodeBlocks["PreList"].GeneratedContents = "";
					m_formatter.CodeBlocks["PostList"].GeneratedContents = "";
				}
				else
				{
					//For bizobjlist
					if(merge != MergeOption.none)
						m_formatter.CodeBlocks["BizObjListDek"].GeneratedContents = "";
					else
						m_formatter.CodeBlocks["MergeBizObjListDek"].GeneratedContents = "";

					m_formatter.CodeBlocks["formatBizObj"].GeneratedContents = "";
					m_formatter.CodeBlocks["formatBizObj2"].GeneratedContents = "";
					m_formatter.CodeBlocks["formatBizObjMerge"].GeneratedContents = "";

					if (parent == null && merge == MergeOption.none)
					{
						n = req.SelectSingleNode(string.Format(s_findCodeXPath, "bizObjListType", name));
						if(n == null)
							m_formatter.CodeBlocks["formatBizObjList"].CodeBlocks["CreateListTypeCustomCode"].setFormatedContents(name);
						else
							m_formatter.CodeBlocks["formatBizObjList"].CodeBlocks["CreateListTypeCustomCode"].GeneratedContents = n.InnerText;
					}
					else
						m_formatter.CodeBlocks["formatBizObjList"].GeneratedContents = "";

					m_formatter.CodeBlocks["FormatReturnObj"].GeneratedContents = "";
					m_formatter.CodeBlocks["getBizObjObj"].GeneratedContents = "";

					if (merge == MergeOption.none)
					{
						m_formatter.CodeBlocks["getBizObjMerge"].GeneratedContents = "";
						m_formatter.CodeBlocks["formatBizObjListMerge"].GeneratedContents = "";
						if (parent == null)
							m_formatter.CodeBlocks["getBizObjList"].CodeBlocks["setupList"].GeneratedContents = "";
						else
							m_formatter.CodeBlocks["getBizObjList"].CodeBlocks["setupList"].setFormatedContents(name);
					}
					else
					{
						m_formatter.CodeBlocks["formatBizObjListMerge"].setFormatedContents(mergeName);
						m_formatter.CodeBlocks["getBizObjList"].GeneratedContents = "";
						if (merge == MergeOption.matchMerge)
							m_formatter.CodeBlocks["getBizObjList2"].GeneratedContents = "";
					}
				}
				string keyName = parent != null ? name : "";
				if (info.KeyFields.Count == 1)
				{
					keyName += ((IBizObjField)info.KeyFields[0]).Name;
					m_formatter.CodeBlocks["SingleFieldKey"].setFormatedContents(keyName.ToUpper());
					m_formatter.CodeBlocks["MultiFieldKeyPre"].GeneratedContents = "";
					m_formatter.CodeBlocks["MultiFieldKey"].GeneratedContents = "";
					m_formatter.CodeBlocks["MultiFieldKeyPost"].GeneratedContents = "";
				}
				else
				{
					keyName = keyName.ToUpper();
					m_formatter.CodeBlocks["SingleFieldKey"].GeneratedContents = "";
					m_formatter.CodeBlocks["MultiFieldKey"].setFormatedContents(keyName + ((IBizObjField)info.KeyFields[0]).Name.ToUpper());
					for (int pos = 1; pos < info.KeyFields.Count; pos++ )
						m_formatter.CodeBlocks["MultiFieldKey"].appendFormatedContents(keyName + ((IBizObjField)info.KeyFields[pos]).Name.ToUpper());
				}
				n = req.SelectSingleNode("FormatterInfo/@" + name + "TypeField");
				if (n == null)
				{
					n = req.SelectSingleNode(string.Format(s_findCodeXPath, "bizObjType", name));
					if (n == null)
					{
						m_formatter.CodeBlocks["CreateHomoType"].setFormatedContents(info.Name);
						m_formatter.CodeBlocks["CreateTypeFromField"].GeneratedContents = "";
					}
					else
					{
						m_formatter.CodeBlocks["CreateHomoType"].GeneratedContents = "";
						m_formatter.CodeBlocks["CreateTypeFromField"].GeneratedContents = "";
						m_formatter.CodeBlocks["CreateTypeCustomCode"].GeneratedContents = n.InnerText;
					}
				}
				else
				{
					m_formatter.CodeBlocks["CreateHomoType"].GeneratedContents = "";
					m_formatter.CodeBlocks["CreateTypeFromField"].setFormatedContents(n.InnerText);
				}

				if (parent == null)
				{
					m_formatter.CodeBlocks["ParentNode"].GeneratedContents = "";
					m_formatter.CodeBlocks["ConstructorParam"].GeneratedContents = "";
					m_formatter.CodeBlocks["SetupParam"].GeneratedContents = "";
				}
				else
				{
					m_formatter.CodeBlocks["ParentNode"].CodeBlocks["Type"].GeneratedContents = parentClassName;
					m_formatter.CodeBlocks["ConstructorParam"].CodeBlocks["Type"].GeneratedContents = parentClassName;
				}


				bool foundFirst = false;
				while (children != null && children.Length > 0)
				{
					if ((mr = app.resMgr.matches(s_bizObjSpecPattern, children)) != null)
					{
						NewAdoNetBizObjFormatter cf = new NewAdoNetBizObjFormatter("", MergeOption.none, null,
							(mr.Groups[2].Value == null) ? mr.Groups[1].Value : mr.Groups[1].Value + "(" + mr.Groups[2].Value + ")",
							info, m_formatter.CodeBlocks["$ClassName"].GeneratedContents, req);
						string cName = mr.Groups[1].Value;
						if (!foundFirst)
						{
							m_formatter.CodeBlocks["AddChildNode"].setFormatedContents(cName, cName + "Formatter(this)");
							m_formatter.CodeBlocks["ChildNodeInfoTypes"].setFormatedContents(cf.FormatterClassCode);
						}
						else
						{
							m_formatter.CodeBlocks["AddChildNode"].appendFormatedContents(cName, cName + "Formatter(this)");
							m_formatter.CodeBlocks["ChildNodeInfoTypes"].appendFormatedContents(cf.FormatterClassCode);
						}
						foundFirst = true;
						children = children.Substring(mr.Length);
					}
				}
				if (!foundFirst)
				{
					m_formatter.CodeBlocks["AddChildNode"].GeneratedContents = "";
					m_formatter.CodeBlocks["ChildNodeInfoTypes"].GeneratedContents = "";
				}
			}
		}

		public string FormatterClassCode
		{
			get { return m_formatter.generateClassText(); }
		}

		public string FormatterClassName
		{
			get { return m_formatter.CodeBlocks["$ClassName"].GeneratedContents; }
		}
	}
}
